﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace SyncUdp.Network
{
    using InspectionRes = Enum.MainMenuInputsInspectionResult;
    using Enum;
    using Event.GameEvent;
    using Interface;
    using MethodExtension;
    using Utility;
    using System.Net;
    using UnityEditor;
    using System.Threading.Tasks;
    using System.Net.Sockets;
    using System.Threading;
    using SyncUdp.Event.NetworkEvent;

    /// <summary>
    /// 网络系统的接口
    /// </summary>
    public interface INetworkSystem : ISingleton
    {
        /// <summary>
        /// 是否为主机
        /// </summary>
        public bool IsHost { get; }

        public int CurFrame { get; set; }

        public int PlayerId { get; }

        public string PlayerName { get; }

        public bool TryParseIP(string ipStr, out IPAddress ip);

        public bool TryParsePort(string portStr, out int port);

        public InspectionRes CreateGame(CreateNetworkArgs args);

        public Task<InspectionRes> JoinGame(CreateNetworkArgs args);

        public void CloseGame();
    }

    /// <summary>
    /// 网络系统
    /// </summary>
    public class NetworkSystem : AbstractSingleton, INetworkSystem
    {
        bool _isHost;
        IGameManager _gm;
        GameUdpServer _server;
        GameUdpClient _client;

        async Task<InspectionRes> TryToJoinGame(CreateNetworkArgs args)
        {
            bool flag = TryParseIP(args.ipStr, out var ip);
            if (flag == false)
                return InspectionRes.IPError;
            flag = TryParsePort(args.portStr, out int port);
            if (flag == false)
                return InspectionRes.PortError;
            var serverIP = new IPEndPoint(ip, port);
            var client = new UdpClient(new IPEndPoint(ip, port - 1));
            // 根据次数，发送信息判断是否人满
            var eventArgs = new NetworkEventToSend()
            {
                eventType = NetworkEventType.TryJoiningGame,
                tryJoinningGameEvent = new TryJoinningGameEvent()
                {
                    playerId = args.playerId, 
                    playerName = args.playerName,
                }
            };
            var jsonUtility = this.GetInstance<IJsonUtility>();
            var buffer = jsonUtility.GetBytesFromObjectWithUTF8(eventArgs);
            int waitingTime = (int)(_gm.CommonData.clientCheckDisconnectionTime * 1000);
            for (int i = 0; i < _gm.CommonData.clientReconnectionCount; i++)
            {
                client.Connect(serverIP);
                await client.SendAsync(buffer, buffer.Length);
                UdpReceiveResult res;
                CancellationTokenSource cts = new CancellationTokenSource();
                var task = Task.Run(async () =>
                {
                    res = await client.ReceiveAsync();
                }, cts.Token);
                Task.WaitAny(task, Task.Delay(waitingTime));
                if (task.IsCompletedSuccessfully)
                {
                    var msg = jsonUtility.GetObjectFromBytesWithUTF8<NetworkEventToSend>(res.Buffer);
                    var type = msg.tryJoinningGameEvent.errorType;
                    switch (type)
                    {
                        case JoinGameErrorType.None: break;
                        case JoinGameErrorType.NotAvailable: return InspectionRes.NotAvailable;
                        case JoinGameErrorType.NotResponsible: return InspectionRes.NotRespondedible;
                        default:
                            if (_gm.IsEditorMode)
                                Debug.LogError($"尝试加入游戏时服务器回应的枚举错误：{type}");
                            return InspectionRes.UnknownError;
                    }
                    var eventCenter = IOCContainer.GetInstance<IEventCenter>();
                    eventCenter.SendEventOnNextFrame(new GameJoinedSuccessfullyEvent()
                    {
                        udpClient = client,
                        playerId = args.playerId, 
                        playerName = args.playerName,
                        otherIds = msg.tryJoinningGameEvent.otherPlayers, 
                        otherNames = msg.tryJoinningGameEvent.otherPlayersName, 
                    });
                    return InspectionRes.None;
                }
                else
                    cts.Cancel();
                cts.Dispose();
            }
            return InspectionRes.NotRespondedible;
        }

        #region 方法实现
        public bool IsHost { get => _isHost; }

        public int PlayerId => _client == null ? -1 : _client.PlayerId;

        public string PlayerName => _client == null ? string.Empty : _client.PlayerName;

        public int CurFrame 
        {
            get => _client == null ? -1 : _client.HandledFrames;
            set
            {
                if (_client != null)
                    _client.HandledFrames = value;
            }
        }

        protected override void OnInit()
        {
            _isHost = false;
            _gm = this.GetInstance<IGameManager>();
            RegisterEvent<GameQuitEvent>(OnGameQuit);
            RegisterEvent<PlayerModelReceiveInputsEvent>(OnPlayerModelReceivedInputs);
            RegisterEvent<GameJoinedSuccessfullyEvent>(OnJoinedGame);
        }

        public bool TryParseIP(string ipStr, out IPAddress ip)
        {
            if (IPAddress.TryParse(ipStr, out ip))
                return true;
            ip = null;
            return false;
        }

        public bool TryParsePort(string portStr, out int port)
        {
            if (int.TryParse(portStr, out port))
                return true;
            port = -1;
            return false;
        }

        public InspectionRes CreateGame(CreateNetworkArgs args)
        {
            bool flag = TryParseIP(args.ipStr, out var ip);
            if (flag == false)
                return InspectionRes.IPError;
            flag = TryParsePort(args.portStr, out int port);
            if (flag == false)
                return InspectionRes.PortError;
            flag = int.TryParse(args.maxPlayerCountStr, out var maxCount);
            if (flag == false)
                return InspectionRes.MaxCountError;
            // 创建 UDP 对象
            // 服务器
            var serverIP = new IPEndPoint(ip, port);
            _server = new GameUdpServer(serverIP, maxCount);
            _server.Start(_gm);
            // 客户端
            _client = new GameUdpClient(new IPEndPoint(ip, port + 1), serverIP, args.playerId, args.playerName);
            _client.Start(_gm);
            _isHost = flag;
            return InspectionRes.None;
        }

        public Task<InspectionRes> JoinGame(CreateNetworkArgs args)
        {
            return Task.Run<InspectionRes>(async () => await TryToJoinGame(args));
        }

        public void CloseGame()
        {
            if (_isHost)
                _server?.Close();
            _client?.Close();
        }

        #endregion

        #region 回调方法
        void OnGameQuit(GameQuitEvent e)
        {
            // 清理 UDP 对象
            _server?.Close();
            _client?.Close();
        }

        void OnPlayerModelReceivedInputs(PlayerModelReceiveInputsEvent e)
        {
            _client.SendInputs(e.inputs);
        }

        void OnJoinedGame(GameJoinedSuccessfullyEvent e)
        {
            _client = new GameUdpClient(e.udpClient, e.playerId, e.playerName);
            _client.Start(_gm);
        }
        #endregion
    }

    /// <summary>
    /// 创建网络所需的参数
    /// </summary>
    public struct CreateNetworkArgs
    {
        public string ipStr;

        public string portStr;

        public string maxPlayerCountStr;

        public int playerId;

        public string playerName;
    }
}
